package com.shop.cereshop.app.service.recommend.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.github.houbb.sensitive.word.core.SensitiveWordHelper;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.shop.cereshop.app.config.ConstantProperties;
import com.shop.cereshop.app.dao.buyer.BuyerUserBlacklistMapper;
import com.shop.cereshop.app.dao.recommend.*;
import com.shop.cereshop.app.page.recommend.CereRecommendCommentPageVO;
import com.shop.cereshop.app.page.recommend.CereRecommendTrendDetailVO;
import com.shop.cereshop.app.page.recommend.MyCommentPageVO;
import com.shop.cereshop.app.page.recommend.RecommendCommentVO;
import com.shop.cereshop.app.param.recommend.MyCommentPageParam;
import com.shop.cereshop.app.param.recommend.RecommendCommentAddDTO;
import com.shop.cereshop.app.param.recommend.RecommendCommentPageParam;
import com.shop.cereshop.app.param.recommend.RecommendCommentSaveParam;
import com.shop.cereshop.app.redis.service.api.StringRedisService;
import com.shop.cereshop.app.service.recommend.CereRecommendCommentService;
import com.shop.cereshop.app.tencent.im.service.MsgService;
import com.shop.cereshop.app.tencent.moderation.service.ContentCheckService;
import com.shop.cereshop.app.utils.RedisUtil;
import com.shop.cereshop.commons.constant.CoReturnFormat;
import com.shop.cereshop.commons.constant.RedisConstants;
import com.shop.cereshop.commons.domain.buyer.CereBuyerUser;
import com.shop.cereshop.commons.domain.common.Page;
import com.shop.cereshop.commons.domain.recommend.CereRecommendComment;
import com.shop.cereshop.commons.domain.recommend.RecommendAtUser;
import com.shop.cereshop.commons.enums.RedisEnum;
import com.shop.cereshop.commons.enums.recommend.RecommendLikeTypeEnum;
import com.shop.cereshop.commons.exception.CoBusinessException;
import com.shop.cereshop.commons.result.Result;
import com.shop.cereshop.commons.tencent.im.enums.notice.SysNoticeTargetTypeEnum;
import com.shop.cereshop.commons.tencent.im.enums.notice.SysNoticeTypeEnum;
import com.shop.cereshop.commons.utils.EmptyUtils;
import com.shop.cereshop.commons.utils.TimeUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.ibatis.annotations.Param;
import org.jetbrains.annotations.Nullable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

@Service
@Slf4j
public class CereRecommendCommentServiceImpl implements CereRecommendCommentService {

    @Resource
    private CereRecommendCommentDAO cereRecommendCommentDAO;
    @Resource
    private CereRecommendTrendsDAO cereRecommendTrendsDAO;
    @Resource
    private CereRecommendCommentLikesDAO cereRecommendCommentLikesDAO;
    @Resource
    private StringRedisService stringRedisService;
    @Resource
    private RecommendAtUserDAO recommendAtUserDAO;
    @Resource
    private CereRecommendLikesDAO cereRecommendLikesDAO;
    @Resource
    private RedisUtil redisUtil;
    @Resource
    private MsgService msgService;
    @Resource
    private ContentCheckService contentCheckService;
    @Resource
    private BuyerUserBlacklistMapper buyerUserBlacklistMapper;

    @Override
    @Transactional(isolation = Isolation.DEFAULT, propagation = Propagation.REQUIRED, rollbackFor = {CoBusinessException.class, Exception.class})
    public CereRecommendCommentPageVO saveComment(RecommendCommentSaveParam param) {
        CereRecommendComment saveComment = new CereRecommendComment();
        saveComment.setRecommendId(param.getRecommendId());
        saveComment.setContent(param.getContent());
        saveComment.setUserId(param.getUserId());
        saveComment.setUserType(1);
        if (ObjectUtil.isNotNull(param.getRecommendCommentId())) {
            CereRecommendComment parentComment = cereRecommendCommentDAO.getCommentById(param.getRecommendCommentId());
            if (ObjectUtil.isNotNull(parentComment)) {
                saveComment.setParentCommentId(param.getRecommendCommentId());
                saveComment.setRootCommentId(ObjectUtil.isNotNull(parentComment.getRootCommentId()) ?
                        parentComment.getRootCommentId() : param.getRecommendCommentId());
                saveComment.setTargetUserId(parentComment.getUserId());
                saveComment.setTargetUserType(parentComment.getUserType());
                if (ObjectUtil.isNotNull(parentComment.getRootCommentId())) {
                    // 增加根评论回复数
                    cereRecommendCommentDAO.addReplyCount(saveComment.getRootCommentId());
                }
                // 增加回复数
                cereRecommendCommentDAO.addReplyCount(param.getRecommendCommentId());
            }
        }
        cereRecommendCommentDAO.saveComment(saveComment);
        cereRecommendTrendsDAO.addCommentCount(param.getRecommendId());
        return cereRecommendCommentDAO.getCommentPageInfo(saveComment.getRecommendCommentId());
    }

    @Override
    @Transactional(isolation = Isolation.DEFAULT, propagation = Propagation.REQUIRED, rollbackFor = {CoBusinessException.class, Exception.class})
    public void deleteComment(Long recommendCommentId) throws CoBusinessException {
        if (ObjectUtil.isNull(recommendCommentId)) {
            throw new CoBusinessException(CoReturnFormat.PARAM_INVALID);
        }
        CereRecommendComment comment = cereRecommendCommentDAO.getCommentById(recommendCommentId);
        CereRecommendTrendDetailVO trends = cereRecommendTrendsDAO.getTrendById(comment.getRecommendId());
        Integer count;
        // 当前评论为根评论，删除包含自己的下级所有评论
        if (ObjectUtil.isNull(comment.getRootCommentId())) {
            count = cereRecommendCommentDAO.deleteCommentByRootCommentId(comment.getRecommendCommentId());
        } else {
            count = cereRecommendCommentDAO.deleteCommentByParentCommentId(comment.getRecommendCommentId());
            // 减少根评论回复数
            cereRecommendCommentDAO.subReplyCount(comment.getRootCommentId(), count);
            if (!comment.getRootCommentId().equals(comment.getParentCommentId())) {
                // 减少父评论回复数
                cereRecommendCommentDAO.subReplyCount(comment.getParentCommentId(), 1);
            }
        }
        redisUtil.decrementLike(RedisEnum.USER_COMMENT_KEY.getKey(), String.valueOf(trends.getRecommendId()));
        cereRecommendTrendsDAO.subCommentCount(comment.getRecommendId(), trends.getCommentCount() - count >= 0 ? count : 0);
    }

    @Override
    public Page<CereRecommendCommentPageVO> commentPage(RecommendCommentPageParam param) {
        PageHelper.startPage(param.getPage(), param.getPageSize());
        PageInfo<CereRecommendCommentPageVO> pageInfo = new PageInfo<>(cereRecommendCommentDAO.commentPage(param));
        Page<CereRecommendCommentPageVO> page = new Page(pageInfo.getList(), pageInfo.getTotal());
        return page;
    }

    @Override
    public CereRecommendCommentPageVO commentTop(RecommendCommentPageParam param) {
        CereRecommendCommentPageVO vo = cereRecommendCommentDAO.getCommentPageInfo(param.getReplyCommentId());
        if ((ObjectUtil.isNull(vo.getParentCommentId()) && ObjectUtil.isNull(param.getRecommendCommentId())) ||
                (ObjectUtil.isNotNull(vo.getParentCommentId()) && ObjectUtil.isNotNull(param.getRecommendCommentId()))) {
            return vo;
        }
        return null;
    }

    @Override
    public Integer getUnreadCount(Long userId) {
        return cereRecommendCommentDAO.getUnreadCount(userId);
    }

    @Override
    public Page<MyCommentPageVO> myMessage(MyCommentPageParam param) {
        PageHelper.startPage(param.getPage(), param.getPageSize());
        PageInfo<MyCommentPageVO> pageInfo = new PageInfo<>(cereRecommendCommentDAO.myMessage(param));
        Page<MyCommentPageVO> page = new Page(pageInfo.getList(), pageInfo.getTotal());
        cereRecommendCommentDAO.read(param.getUserId());
        return page;
    }

    @Override
    public Boolean getCommentStatus(Long recommendCommentId) {
        CereRecommendComment comment = cereRecommendCommentDAO.getCommentById(recommendCommentId);
        boolean status = ObjectUtil.isNotNull(comment);
        if (!status) {
            return status;
        }
        CereRecommendTrendDetailVO trends = cereRecommendTrendsDAO.getTrendById(comment.getRecommendId());
        status = ObjectUtil.isNotNull(trends);
        return status;
    }

    @Override
    @Transactional(isolation = Isolation.DEFAULT, propagation = Propagation.REQUIRED, rollbackFor = {CoBusinessException.class, Exception.class})
    public Result saveRecommendComment(RecommendCommentAddDTO param, CereBuyerUser user) throws CoBusinessException {
        // 判断评论发布者是否被拉黑
        Result<Object> checkBlackResult = checkBlackUser(user.getBuyerUserId(), param);
        if (checkBlackResult != null) {
            return checkBlackResult;
        }

        // 腾讯云内容审核
        if (Boolean.TRUE.equals(ConstantProperties.OPEN_TENCENT_MODERATION)) {
            Result<Object> checkResult = checkContent(param);
            if (checkResult != null) {
                return checkResult;
            }
        }

        //预留审核逻辑
        if (Boolean.TRUE.equals(stringRedisService.get(RedisConstants.RISK_CONTROL))) {
            if (SensitiveWordHelper.contains(param.getContent())) {
                throw new CoBusinessException(CoReturnFormat.CONTENT_SENSITIVE);
            }
        }

        long recommendId = 0L;
        String now = TimeUtils.yyMMddHHmmss();
        param.setUserId(user.getBuyerUserId());
        if (CollectionUtil.isNotEmpty(param.getImageList())) {
            param.setImages(String.join(",", param.getImageList()));
        }
        CereRecommendComment cereRecommendComment = BeanUtil.copyProperties(param, CereRecommendComment.class);
        //判断是否为作者
        Long buyerUserId = cereRecommendTrendsDAO.getBuyerUserId(cereRecommendComment.getRecommendId());
        cereRecommendComment.setIfAuthor(user.getBuyerUserId().equals(buyerUserId) ? 1 : 0);
        cereRecommendComment.setCreateTime(now);
        cereRecommendComment.setUserType(1);
        cereRecommendComment.setTargetUserType(1);
        cereRecommendComment.setStatue(0);

        //判断是否是一级评论
        if (param.getParentCommentId() == null) {
            recommendId = cereRecommendComment.getRecommendId();
            cereRecommendCommentDAO.insert(cereRecommendComment);

            // 获取动态发布者id
            Long trendsUserId = cereRecommendTrendsDAO.getBuyerUserId(recommendId);

            //一级评论：根目录id为自身id  先设置为null
            cereRecommendComment.setRootCommentId(cereRecommendComment.getRecommendCommentId());
            // 如果ID是自动生成的，现在可以获取到
            if (cereRecommendComment.getRecommendCommentId() != null) {
                // 使用LambdaUpdateWrapper直接更新，避免全字段更新
                LambdaUpdateWrapper<CereRecommendComment> wrapper = new LambdaUpdateWrapper<>();
                wrapper.eq(CereRecommendComment::getRecommendCommentId, cereRecommendComment.getRecommendCommentId())
                        .set(CereRecommendComment::getRootCommentId, cereRecommendComment.getRecommendCommentId())
                        .set(CereRecommendComment::getTargetUserId, trendsUserId);
                cereRecommendCommentDAO.update(null, wrapper);
            }

            // 发送系统通知给动态作者，有人评论了动态
            msgService.sendNotice(trendsUserId, SysNoticeTypeEnum.COMMENT.getCode(), SysNoticeTargetTypeEnum.POSTS.getCode(),
                    String.valueOf(recommendId), user.getBuyerUserId(), user.getName());

        } else {
            //二级/嵌套评论：继承评论的根评论
            //根据父级评论的id获取父级评论部分信息
            CereRecommendComment comment = cereRecommendCommentDAO.selectCommentInfo(param.getParentCommentId());
            recommendId = comment.getRecommendId();
            cereRecommendComment.setRootCommentId(comment.getRootCommentId());
            cereRecommendComment.setParentCommentId(param.getParentCommentId());
            cereRecommendComment.setTargetUserId(comment.getUserId());
            cereRecommendComment.setRecommendId(comment.getRecommendId());
            cereRecommendComment.setStatue(0);
            cereRecommendCommentDAO.insert(cereRecommendComment);

            // 发送系统通知给原评论作者，有人回复了评论
            msgService.sendNotice(comment.getUserId(), SysNoticeTypeEnum.REPLY.getCode(), SysNoticeTargetTypeEnum.COMMENT.getCode(),
                    String.valueOf(cereRecommendComment.getRecommendCommentId()), user.getBuyerUserId(), user.getName());
        }

        //更新父评论的回复数
        if (param.getRootCommentId() != null) {
            cereRecommendCommentDAO.incrementReplyCount(param.getRootCommentId());
        }
        //插入@关系
        if (param.getAtUser() != null && CollectionUtil.isNotEmpty(param.getAtUser())) {
            //批量插入
            List<RecommendAtUser> atUsers = param.getAtUser().entrySet().stream()
                    .map(entry -> {
                        RecommendAtUser atUser = new RecommendAtUser();
                        atUser.setAtUserId(entry.getKey())
                                .setAtUsername(entry.getValue())
                                .setAtTargetId(cereRecommendComment.getRecommendCommentId())
                                .setBuyerUserId(user.getBuyerUserId())
                                .setAtType(2);
                        return atUser;
                    })
                    .collect(Collectors.toList());

            if (!atUsers.isEmpty()) {
                recommendAtUserDAO.insertBatch(atUsers);
            }
        }
        redisUtil.incrementLike(RedisEnum.USER_COMMENT_KEY.getKey(), String.valueOf(recommendId));
        return new Result(CoReturnFormat.SUCCESS);
    }

    /**
     * 检测用户是否被拉黑
     * @param userId 发布评论的用户id
     * @param param
     * @return
     */
    private Result<Object> checkBlackUser(Long userId, RecommendCommentAddDTO param) {
        List<Long> blockMeUserIds = buyerUserBlacklistMapper.getBlockMeUserIds(userId);
        if (CollUtil.isEmpty(blockMeUserIds)) {
            return null;
        }
        //判断是否是一级评论
        if (param.getParentCommentId() == null) {
            // 获取动态发布者id
            Long trendsUserId = cereRecommendTrendsDAO.getBuyerUserId(param.getRecommendId());
            if (blockMeUserIds.contains(trendsUserId)) {
                return Result.failed(CoReturnFormat.FAIL, "您已被拉黑，无法评论");
            }
        } else {
            //根据父级评论的id获取父级评论部分信息
            CereRecommendComment comment = cereRecommendCommentDAO.selectCommentInfo(param.getParentCommentId());
            if (comment == null) {
                return null;
            }
            if (blockMeUserIds.contains(comment.getUserId())) {
                return Result.failed(CoReturnFormat.FAIL, "您已被拉黑，无法评论");
            }
        }
        return null;
    }

    /**
     * 检测内容合规性
     * @param param
     * @return
     */
    private Result<Object> checkContent(RecommendCommentAddDTO param) {
        // 文本内容审核 检查是否违规
        Result<Object> textCheckResult = contentCheckService.checkTextContent(param.getContent());
        if (!CoReturnFormat.SUCCESS.equals(textCheckResult.getCode())) {
            return textCheckResult;
        }

        // 单张图片审核 检查是否违规
        if (StringUtils.isNotBlank(param.getImages())) {
            Result<Object> imageCheckResult = contentCheckService.checkSingleImage(param.getImages());
            if (!CoReturnFormat.SUCCESS.equals(imageCheckResult.getCode())) {
                return imageCheckResult;
            }
        }

        // 多张图片批量审核 检查是否违规
        if (CollUtil.isNotEmpty(param.getImageList())) {
            Result<Object> batchImageCheckResult = contentCheckService.checkBatchImages(param.getImageList());
            if (!CoReturnFormat.SUCCESS.equals(batchImageCheckResult.getCode())) {
                return batchImageCheckResult;
            }
        }
        return null;
    }


    @Override
    public Page<RecommendCommentVO> getRecommendCommentPage(Integer page, Integer pageSize, Long recommendId, Long parentCommentId, CereBuyerUser user) {
        // todo: 查询redis热门评论

        //设置分页参数
        List<RecommendCommentVO> recommendPageVO = Collections.emptyList();
        if (recommendId != null) {
            //查询一级评论列表
            PageHelper.startPage(page, pageSize);
            recommendPageVO = cereRecommendCommentDAO.getRecommendCommentPage(recommendId, null);
            recommendPageVO = recommendPageVO.stream()
                    .filter(comment -> comment.getStatue() != 1)
                    .collect(Collectors.toList());

        }
        if (parentCommentId != null) {
            //查询二级以及更深评论列表
            PageHelper.startPage(page, pageSize);
            recommendPageVO = cereRecommendCommentDAO.getRecommendCommentPage(null, parentCommentId);
            recommendPageVO = recommendPageVO.stream()
                    .filter(comment -> comment.getStatue() != 1)
                    .collect(Collectors.toList());

        }
        //如果查询结果为空，直接返回
        if (EmptyUtils.isEmpty(recommendPageVO)) {
            PageInfo<RecommendCommentVO> emptyPageInfo = new PageInfo<>(new ArrayList<>());
            return new Page<>(emptyPageInfo.getList(), emptyPageInfo.getTotal());
        }

        try {
            //每个评论都查询用户是否点赞和点赞数量
            //先将recommendPageVO的评论id收集为集合
            List<Long> recommendCommentIds = recommendPageVO.stream()
                    .map(RecommendCommentVO::getRecommendCommentId)
                    .filter(Objects::nonNull)
                    .collect(Collectors.toList());

            //批量查询用户点赞信息和该评论的点赞数量
            if (EmptyUtils.isEmpty(recommendCommentIds)) {
                PageInfo<RecommendCommentVO> pageInfo = new PageInfo<>(recommendPageVO);
                return new Page<>(pageInfo.getList(), pageInfo.getTotal());
            }
            List<RecommendCommentVO> userLikeList = cereRecommendLikesDAO.getLikeStatusAndCount(user.getBuyerUserId(),
                    recommendCommentIds,
                    RecommendLikeTypeEnum.COMMENT.getCode());

            //根据批量查出的点赞信息，将点赞信息设置到对应的评论对象中
            Map<Long, RecommendCommentVO> userLikeMap = userLikeList.stream()
                    .collect(Collectors
                            .toMap(RecommendCommentVO::getRecommendCommentId, Function.identity(), (existing, replacement) -> existing));

            for (RecommendCommentVO recommendCommentVO : recommendPageVO) {
                RecommendCommentVO userLike = userLikeMap.get(recommendCommentVO.getRecommendCommentId());
                if (userLike != null) {
                    recommendCommentVO.setLikeStatus(userLike.getLikeStatus());
                    recommendCommentVO.setLikeCount(userLike.getLikeCount());
                } else {
                    // 如果userLike为空，说明该用户没有对该评论进行任何操作
                    recommendCommentVO.setLikeStatus(0);
                    recommendCommentVO.setLikeCount(0);
                }
                // todo：这里发布时间需要展示审核后的，目前先使用创建时间
                recommendCommentVO.setPublishTime(recommendCommentVO.getCreateTime());
            }
        } catch (Exception e) {
            //如果查询点赞信息失败，设置默认值
            recommendPageVO.forEach(vo -> {
                if (vo != null) {
                    vo.setLikeStatus(0);
                    vo.setLikeCount(0);
                }
            });
            log.error("查询用户点赞信息失败", e);
        }

        PageInfo<RecommendCommentVO> pageInfo = new PageInfo<>(recommendPageVO);
        Page<RecommendCommentVO> result = new Page(pageInfo.getList(), pageInfo.getTotal());
        return result;
    }

    /**
     * 删除动态下的评论
     *
     * @param recommendId
     */
    @Override
    @Transactional(isolation = Isolation.DEFAULT, propagation = Propagation.REQUIRED, rollbackFor = {CoBusinessException.class, Exception.class})
    public void removeByRecommendId(Long recommendId) {
        cereRecommendCommentDAO.removeByRecommendId(recommendId);
    }
}
